package edu.ntu.cloudroid.xacml.obligation;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;


import com.sun.xacml.Obligation;
import com.sun.xacml.attr.IntegerAttribute;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;

import edu.ntu.cloudroid.model.IDs;
import edu.ntu.cloudroid.model.data.AbstractData;
import edu.ntu.cloudroid.model.data.DetachedData;
import edu.ntu.cloudroid.model.data.VitalSignData;
import edu.ntu.cloudroid.model.data.VitalSignInfo;
import edu.ntu.cloudroid.model.function.stats.IDataStats;
import edu.ntu.cloudroid.model.policy.SimpleFiltering;
import edu.ntu.cloudroid.model.profile.ProfileData;
import edu.ntu.cloudroid.server.backendservice.DataService;
import edu.ntu.cloudroid.server.backendservice.FriendService;
import edu.ntu.cloudroid.xacml.Utilities;
import edu.ntu.cloudroid.xacml.XACMLException;
import edu.ntu.cloudroid.xacml.XACMLStrings;

/**
 * Simple filtering, for location data. Retrieve all data
 * 
 */
public class SlidingWindowObligation implements ObligationHandle {

	
	@Override
	public List<DetachedData> processObligation(ProfileData subject, ProfileData resource,
			RequestCtx requestCtx, Obligation o) throws Exception {
		List<Attribute> assignments = o.getAssignments();
		int windowSize=0,advanceStep=0;	
		int colIdx=0; 
		int funcId = 0;
		
		Date startingDate = null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
		sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		
		for (Attribute a : assignments) {
			if (a.getId().equals(new URI(XACMLStrings.ATTR_SW_size))) {
				IntegerAttribute attr = (IntegerAttribute) a.getValue();
				windowSize = (int) attr.getValue();
			} 
			if (a.getId().equals(new URI(XACMLStrings.ATTR_Stat_FunctionId))) {
				IntegerAttribute attr = (IntegerAttribute) a.getValue();
				funcId = (int) attr.getValue();
			}
			if (a.getId().equals(new URI(XACMLStrings.ATTR_Filtering_ColumnIdx))) {
				IntegerAttribute attr = (IntegerAttribute) a.getValue();
				colIdx = (int) attr.getValue();
			}
			else if (a.getId().equals(
					new URI(XACMLStrings.ATTR_SW_start))) {
				StringAttribute sa = (StringAttribute) a.getValue();
				startingDate = sdf.parse(sa.getValue());
			}
			else if (a.getId().equals(new URI(XACMLStrings.ATTR_SW_advance))){
				IntegerAttribute attr = (IntegerAttribute) a.getValue();
				advanceStep = (int) attr.getValue();
			}
		}
		return this.evaluateSummarySWCondition(subject, resource, windowSize,
				advanceStep, startingDate,colIdx,IDs.getStatsFunction(funcId));
	}

	private List<DetachedData> evaluateSummarySWCondition(ProfileData subject,
			ProfileData resource, int windowSize, int advanceStep,
			Date startingDate, int colIdx, IDataStats func) throws Exception {
		int dataId = resource.getDataId();
		Date latestTimeStamp = FriendService.getFriendTimeStamp(resource.getEmailAddress(), subject.getEmailAddress());
		if (latestTimeStamp.before(startingDate))
			latestTimeStamp = startingDate;
		List<AbstractData> data = DataService.getData(resource, latestTimeStamp, dataId);
		int maxIdx = data.size();
		if (maxIdx<windowSize)
			return new ArrayList<DetachedData>();//empty result;
		
		int startingIdx = 0, i=0;
		List<DetachedData> returnedList = new ArrayList<DetachedData>();
		do{
			ArrayList<AbstractData> temp = new ArrayList<AbstractData>();
			i = startingIdx;
			for (i=startingIdx; i<startingIdx+windowSize; i++){				
				temp.add(data.get(i));
			}				
			DetachedData summary = ((AbstractData) func.getStats(temp, colIdx))
					.detach(colIdx);
			returnedList.add(summary);
			startingIdx = startingIdx + advanceStep;
		}while (startingIdx+windowSize<=maxIdx);
		
		//Update friend to the latest accessible time stamp
		startingIdx--;
		AbstractData latestData = data.get(startingIdx);
		FriendService.delayTimestampUpdate(resource.getEmailAddress(),
				subject.getEmailAddress(), latestData.getTimestamp());
		return returnedList;
	}		
}
